#include <pthread.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#include <pthread.h>
#include <time.h>
#include <semaphore.h>

#include <imp/imp_log.h>
#include <imp/imp_common.h>
#include <imp/imp_system.h>
#include <imp/imp_framesource.h>
#include <imp/imp_encoder.h>

#include "sample-common.h"

#define TAG "Sample-Encoder-changeRcParam-test"

// #define SAVE_STREAM

extern struct chn_conf chn[];
extern int direct_switch;
extern IMPSensorInfo sensor_info;

static IMPEncoderRcMode S_RC_METHOD_TEST = ENC_RC_MODE_CBR;
pthread_t getStreamTid[3+FS_CHN_NUM];
pthread_t changeRcParamTid[FS_CHN_NUM];
int getVideo[] = { 1, 1, 0, 0 };
int getJpeg[] = { 0, 0, 0, 0 };
int changeRcParam[] = { 1, 1, 0, 0 };
static int totalSaveCnt = 0;

#ifdef SAVE_STREAM
static int save_stream_test(int fd, IMPEncoderStream *stream);
static int save_stream_by_name_test(char *stream_prefix, int idx, IMPEncoderStream *stream);
#endif
static void *change_rcparam_chn0_chn1(void *args);
static void *get_video_stream_chn0(void *args);
static void *get_video_stream_chn1(void *args);
static void *get_jpeg_stream_chn3(void *args);

#define SHOW_FRM_BITRATE
#ifdef SHOW_FRM_BITRATE
sem_t sem_countfps;
#endif

int main(int argc, char *argv[])
{
	int i = 0;
	int ret = 0;

	totalSaveCnt = 2000;
	if(argc > 1) {
		totalSaveCnt = atoi(argv[1]);
	}

	sem_init(&sem_countfps, 0, 1);

	direct_switch = 1;
	chn[0].enable = 1;
	chn[1].enable = 1;
	chn[2].enable = 0;

	/* Step.1 System init */
	{
		memset(&sensor_info, 0, sizeof(IMPSensorInfo));
		memcpy(sensor_info.name, SENSOR_NAME, sizeof(SENSOR_NAME));
		sensor_info.cbus_type = SENSOR_CUBS_TYPE;
		memcpy(sensor_info.i2c.type, SENSOR_NAME, sizeof(SENSOR_NAME));
		sensor_info.i2c.addr = SENSOR_I2C_ADDR;
		sensor_info.i2c.i2c_adapter_id = SENSOR_I2C_ADAPTER_ID;

		ret = IMP_ISP_Open();
		if (ret < 0) {
			IMP_LOG_ERR(TAG, "IMP_ISP_Open failed\n");
			return -1;
		}

		ret = IMP_ISP_AddSensor(&sensor_info);
		if (ret < 0) {
			IMP_LOG_ERR(TAG, "IMP_ISP_AddSensor failed\n");
			return -1;
		}

		ret = IMP_ISP_EnableSensor();
		if (ret < 0) {
			IMP_LOG_ERR(TAG, "IMP_ISP_EnableSensor failed\n");
			return -1;
		}

		ret = IMP_System_Init();
		if (ret < 0) {
			IMP_LOG_ERR(TAG, "IMP_System_Init failed\n");
			return -1;
		}

		ret = IMP_ISP_EnableTuning();
		if (ret < 0) {
			IMP_LOG_ERR(TAG, "IMP_ISP_EnableTuning failed\n");
			return -1;
		}
	}

	/* Step.2 FrameSource init */
	{
		for (i = 0; i < FS_CHN_NUM; i++) {
			if (chn[i].enable) {
				ret = IMP_FrameSource_CreateChn(chn[i].index, &chn[i].fs_chn_attr);
				if (ret < 0) {
					IMP_LOG_ERR(TAG, "IMP_FrameSource_CreateChn(%d) failed\n", chn[i].index);
					return -1;
				}

				ret = IMP_FrameSource_SetChnAttr(chn[i].index, &chn[i].fs_chn_attr);
				if (ret < 0) {
					IMP_LOG_ERR(TAG, "IMP_FrameSource_SetChnAttr(%d) failed\n", chn[i].index);
					return -1;
				}
			}
		}
	}

	/* Step.3 Encoder init */
	{
		IMPEncoderCHNAttr chnAttr;
		IMPEncoderAttr *encAttr;
		IMPEncoderRcAttr *rcAttr;
		for (i = 0; i < FS_CHN_NUM; i++) {
			if (chn[i].enable) {
				ret = IMP_Encoder_CreateGroup(chn[i].index);
				if (ret < 0) {
					IMP_LOG_ERR(TAG, "IMP_Encoder_CreateGroup(%d) failed\n", chn[i].index);
					return -1;
				}
			}
		}
		//h264
		for (i = 0; i < FS_CHN_NUM; i++) {
			if (chn[i].enable) {
				memset(&chnAttr, 0, sizeof(IMPEncoderCHNAttr));
				encAttr = &chnAttr.encAttr;
				rcAttr = &chnAttr.rcAttr;
				encAttr->enType = chn[i].payloadType;
				if ((chn[i].fs_chn_attr.picHeight > 1920) || (chn[i].fs_chn_attr.picHeight == 1920)) {
					encAttr->bufSize = chn[i].fs_chn_attr.picWidth*chn[i].fs_chn_attr.picHeight*3/10;
				} else if ((chn[i].fs_chn_attr.picHeight > 1520) || (chn[i].fs_chn_attr.picHeight == 1520)) {
					encAttr->bufSize = chn[i].fs_chn_attr.picWidth*chn[i].fs_chn_attr.picHeight*3/8;
				} else if ((chn[i].fs_chn_attr.picHeight > 1080) || (chn[i].fs_chn_attr.picHeight == 1080)) {
					encAttr->bufSize = chn[i].fs_chn_attr.picWidth*chn[i].fs_chn_attr.picHeight/2;
				} else {
					encAttr->bufSize = chn[i].fs_chn_attr.picWidth*chn[i].fs_chn_attr.picHeight*3/4;
				}
				encAttr->profile = -1;
				encAttr->picWidth = chn[i].fs_chn_attr.picWidth;
				encAttr->picHeight = chn[i].fs_chn_attr.picHeight;
				rcAttr->outFrmRate.frmRateNum = chn[i].fs_chn_attr.outFrmRateNum;
				rcAttr->outFrmRate.frmRateDen = chn[i].fs_chn_attr.outFrmRateDen;
				rcAttr->maxGop = 2 * rcAttr->outFrmRate.frmRateNum / rcAttr->outFrmRate.frmRateDen;
				if (S_RC_METHOD_TEST == ENC_RC_MODE_CBR) {
					rcAttr->attrRcMode.rcMode = ENC_RC_MODE_CBR;
					rcAttr->attrRcMode.attrH264Cbr.outBitRate = BITRATE_720P_Kbs * (chn[i].fs_chn_attr.picWidth * chn[i].fs_chn_attr.picHeight) / (1280 * 720);
					rcAttr->attrRcMode.attrH264Cbr.maxQp = 45;
					rcAttr->attrRcMode.attrH264Cbr.minQp = 15;
					rcAttr->attrRcMode.attrH264Cbr.iBiasLvl = 0;
					rcAttr->attrRcMode.attrH264Cbr.frmQPStep = 3;
					rcAttr->attrRcMode.attrH264Cbr.gopQPStep = 15;
					rcAttr->attrRcMode.attrH264Cbr.adaptiveMode = false;
					rcAttr->attrRcMode.attrH264Cbr.gopRelation = false;

					rcAttr->attrHSkip.hSkipAttr.skipType = IMP_Encoder_STYPE_N1X;
					rcAttr->attrHSkip.hSkipAttr.m = 0;
					rcAttr->attrHSkip.hSkipAttr.n = 0;
					rcAttr->attrHSkip.hSkipAttr.maxSameSceneCnt = 0;
					rcAttr->attrHSkip.hSkipAttr.bEnableScenecut = 0;
					rcAttr->attrHSkip.hSkipAttr.bBlackEnhance = 0;
					rcAttr->attrHSkip.maxHSkipType = IMP_Encoder_STYPE_N1X;
				} else if (S_RC_METHOD_TEST == ENC_RC_MODE_VBR) {
					rcAttr->attrRcMode.rcMode = ENC_RC_MODE_VBR;
					rcAttr->attrRcMode.attrH264Vbr.maxQp = 45;
					rcAttr->attrRcMode.attrH264Vbr.minQp = 15;
					rcAttr->attrRcMode.attrH264Vbr.staticTime = 2;
					rcAttr->attrRcMode.attrH264Vbr.maxBitRate = BITRATE_720P_Kbs * (chn[i].fs_chn_attr.picWidth * chn[i].fs_chn_attr.picHeight) / (1280 * 720);
					rcAttr->attrRcMode.attrH264Vbr.iBiasLvl = 0;
					rcAttr->attrRcMode.attrH264Vbr.changePos = 80;
					rcAttr->attrRcMode.attrH264Vbr.qualityLvl = 2;
					rcAttr->attrRcMode.attrH264Vbr.frmQPStep = 3;
					rcAttr->attrRcMode.attrH264Vbr.gopQPStep = 15;
					rcAttr->attrRcMode.attrH264Vbr.gopRelation = false;

					rcAttr->attrHSkip.hSkipAttr.skipType = IMP_Encoder_STYPE_N1X;
					rcAttr->attrHSkip.hSkipAttr.m = 0;
					rcAttr->attrHSkip.hSkipAttr.n = 0;
					rcAttr->attrHSkip.hSkipAttr.maxSameSceneCnt = 0;
					rcAttr->attrHSkip.hSkipAttr.bEnableScenecut = 0;
					rcAttr->attrHSkip.hSkipAttr.bBlackEnhance = 0;
					rcAttr->attrHSkip.maxHSkipType = IMP_Encoder_STYPE_N1X;
				} else if (S_RC_METHOD_TEST == ENC_RC_MODE_SMART) {
					rcAttr->attrRcMode.rcMode = ENC_RC_MODE_SMART;
					rcAttr->attrRcMode.attrH264Smart.maxQp = 45;
					rcAttr->attrRcMode.attrH264Smart.minQp = 15;
					rcAttr->attrRcMode.attrH264Smart.staticTime = 2;
					rcAttr->attrRcMode.attrH264Smart.maxBitRate = BITRATE_720P_Kbs * (chn[i].fs_chn_attr.picWidth * chn[i].fs_chn_attr.picHeight) / (1280 * 720);
					rcAttr->attrRcMode.attrH264Smart.iBiasLvl = 0;
					rcAttr->attrRcMode.attrH264Smart.changePos = 80;
					rcAttr->attrRcMode.attrH264Smart.qualityLvl = 2;
					rcAttr->attrRcMode.attrH264Smart.frmQPStep = 3;
					rcAttr->attrRcMode.attrH264Smart.gopQPStep = 15;
					rcAttr->attrRcMode.attrH264Smart.gopRelation = false;

					rcAttr->attrHSkip.hSkipAttr.skipType = IMP_Encoder_STYPE_N1X;
					rcAttr->attrHSkip.hSkipAttr.m = rcAttr->maxGop - 1;
					rcAttr->attrHSkip.hSkipAttr.n = 1;
					rcAttr->attrHSkip.hSkipAttr.maxSameSceneCnt = 6;
					rcAttr->attrHSkip.hSkipAttr.bEnableScenecut = 0;
					rcAttr->attrHSkip.hSkipAttr.bBlackEnhance = 0;
					rcAttr->attrHSkip.maxHSkipType = IMP_Encoder_STYPE_N1X;
				} else {
					rcAttr->attrRcMode.rcMode = ENC_RC_MODE_FIXQP;
					rcAttr->attrRcMode.attrH264FixQp.qp = 35;

					rcAttr->attrHSkip.hSkipAttr.skipType = IMP_Encoder_STYPE_N1X;
					rcAttr->attrHSkip.hSkipAttr.m = 0;
					rcAttr->attrHSkip.hSkipAttr.n = 0;
					rcAttr->attrHSkip.hSkipAttr.maxSameSceneCnt = 0;
					rcAttr->attrHSkip.hSkipAttr.bEnableScenecut = 0;
					rcAttr->attrHSkip.hSkipAttr.bBlackEnhance = 0;
					rcAttr->attrHSkip.maxHSkipType = IMP_Encoder_STYPE_N1X;
				}

				if (direct_switch == 1) {
					if (0 == chn[i].index) chnAttr.bEnableIvdc = true;
				}

				ret = IMP_Encoder_CreateChn(chn[i].index, &chnAttr);
				if (ret < 0) {
					IMP_LOG_ERR(TAG, "IMP_Encoder_CreateChn(%d) failed\n", chn[i].index);
					return -1;
				}
			}
		}
		//jpeg
		for (i = 0; i < FS_CHN_NUM; i++) {
			if (chn[i].enable && (chn[i].index == 0)) {
				memset(&chnAttr, 0, sizeof(IMPEncoderCHNAttr));
				encAttr = &chnAttr.encAttr;

				encAttr->enType = PT_JPEG;
				encAttr->bufSize = 0;
				encAttr->profile = 0;
				encAttr->picWidth = chn[i].fs_chn_attr.picWidth;
				encAttr->picHeight = chn[i].fs_chn_attr.picHeight;

				if (direct_switch == 1) {
					if (0 == chn[i].index) chnAttr.bEnableIvdc = true;
				}

				ret = IMP_Encoder_CreateChn(3+chn[i].index, &chnAttr);
				if (ret < 0) {
					IMP_LOG_ERR(TAG, "IMP_Encoder_CreateChn(%d) failed\n", 3+chn[i].index);
					return -1;
				}
			}
		}
		for (i = 0; i < FS_CHN_NUM; i++) {
			if (chn[i].enable) {
				ret = IMP_Encoder_RegisterChn(chn[i].index, chn[i].index);
				if (ret < 0) {
					IMP_LOG_ERR(TAG, "IMP_Encoder_RegisterChn(%d, %d) failed\n", chn[i].index, chn[i].index);
					return -1;
				}
				if (chn[i].index == 0) {
					ret = IMP_Encoder_RegisterChn(chn[i].index, 3+chn[i].index);
					if (ret < 0) {
						IMP_LOG_ERR(TAG, "IMP_Encoder_RegisterChn(%d, %d) failed\n", chn[i].index, 3+chn[i].index);
						return -1;
					}
				}
			}
		}
	}

	/* Step.4 Bind */
	{
		for (i = 0; i < FS_CHN_NUM; i++) {
			if (chn[i].enable) {
				ret = IMP_System_Bind(&chn[i].framesource_chn, &chn[i].imp_encoder);
				if (ret < 0) {
					IMP_LOG_ERR(TAG, "IMP_System_Bind framesourc%d and encoder%d failed\n", chn[i].framesource_chn.groupID, chn[i].imp_encoder.groupID);
					return -1;
				}
			}
		}
	}

	/* Step.5 Stream On */
	{
		for (i = 0; i < FS_CHN_NUM; i++) {
			if (chn[i].enable) {
				ret = IMP_FrameSource_EnableChn(chn[i].index);
				if (ret < 0) {
					IMP_LOG_ERR(TAG, "IMP_FrameSource_EnableChn(%d) failed\n", chn[i].index);
					return -1;
				}
			}
		}
	}

	/* Step.6 Get stream and Snap */
	{
		for (i = 0; i < FS_CHN_NUM; i++) {
			if (chn[i].enable && (chn[i].index == 0)) {
				int arg = (chn[i].payloadType << 16 | chn[i].index);
				ret = pthread_create(&getStreamTid[chn[i].index], NULL, get_video_stream_chn0, (void *)arg);
				if (ret < 0) {
					IMP_LOG_ERR(TAG, "Create Chn%d get_video_stream failed\n", chn[i].index);
					return -1;
				}
				ret = pthread_create(&changeRcParamTid[chn[i].index], NULL, change_rcparam_chn0_chn1, (void *)arg);
				if (ret < 0) {
					IMP_LOG_ERR(TAG, "Create Chn%d change_rcparam_chn0_chn1 failed\n", chn[i].index);
					return -1;
				}
				arg = (PT_JPEG << 16 | (3+chn[i].index));
				ret = pthread_create(&getStreamTid[3+chn[i].index], NULL, get_jpeg_stream_chn3, (void *)arg);
				if (ret < 0) {
					IMP_LOG_ERR(TAG, "Create Chn%d get_jpeg_stream failed\n", 3+chn[i].index);
					return -1;
				}
			} else if (chn[i].enable && (chn[i].index == 1)) {
				int arg = (chn[i].payloadType << 16 | chn[i].index);
				ret = pthread_create(&getStreamTid[chn[i].index], NULL, get_video_stream_chn1, (void *)arg);
				if (ret < 0) {
					IMP_LOG_ERR(TAG, "Create Chn%d get_video_stream failed\n", chn[i].index);
					return -1;
				}
				ret = pthread_create(&changeRcParamTid[chn[i].index], NULL, change_rcparam_chn0_chn1, (void *)arg);
				if (ret < 0) {
					IMP_LOG_ERR(TAG, "Create Chn%d change_rcparam_chn0_chn1 failed\n", chn[i].index);
					return -1;
				}
			}
		}
	}

	{
		for (i = 0; i < FS_CHN_NUM; i++) {
			if (chn[i].enable) {
				pthread_join(getStreamTid[chn[i].index], NULL);
				if (chn[i].index == 0)
					pthread_join(getStreamTid[3+chn[i].index], NULL);
			}
		}
	}

	/* Step.a Stream Off */
	{
		for (i = 0; i < FS_CHN_NUM; i++) {
			if (chn[i].enable) {
				ret = IMP_FrameSource_DisableChn(chn[i].index);
				if (ret < 0) {
					IMP_LOG_ERR(TAG, "IMP_FrameSource_DisableChn(%d) failed\n", chn[i].index);
					return -1;
				}
			}
		}
	}

	/* Step.b UnBind */
	{
		for (i = 0; i < FS_CHN_NUM; i++) {
			if (chn[i].enable) {
				ret = IMP_System_UnBind(&chn[i].framesource_chn, &chn[i].imp_encoder);
				if (ret < 0) {
					IMP_LOG_ERR(TAG, "IMP_System_UnBind framesource%d and encoder%d failed\n", chn[i].framesource_chn.groupID, chn[i].imp_encoder.groupID);
					return -1;
				}
			}
		}
	}

	/* Step.c Encoder exit */
	{
		IMPEncoderCHNStat chnStat;
		for (i = 0; i < FS_CHN_NUM; i++) {
			if (chn[i].enable) {
				memset(&chnStat, 0, sizeof(IMPEncoderCHNStat));
				ret = IMP_Encoder_Query(chn[i].index, &chnStat);
				if (ret < 0) {
					IMP_LOG_ERR(TAG, "IMP_Encoder_Query(%d) failed\n", chn[i].index);
					return -1;
				}

				if (chnStat.registered) {
					ret = IMP_Encoder_UnRegisterChn(chn[i].index);
					if (ret < 0) {
						IMP_LOG_ERR(TAG, "IMP_Encoder_UnRegisterChn(%d) failed\n", chn[i].index);
						return -1;
					}

					ret = IMP_Encoder_DestroyChn(chn[i].index);
					if (ret < 0) {
						IMP_LOG_ERR(TAG, "IMP_Encoder_DestroyChn(%d) failed\n", chn[i].index);
						return -1;
					}
				}
				if (chn[i].index == 0) {
					memset(&chnStat, 0, sizeof(IMPEncoderCHNStat));
					ret = IMP_Encoder_Query(3+chn[i].index, &chnStat);
					if (ret < 0) {
						IMP_LOG_ERR(TAG, "IMP_Encoder_Query(%d) failed\n", 3+chn[i].index);
						return -1;
					}

					if (chnStat.registered) {
						ret = IMP_Encoder_UnRegisterChn(3+chn[i].index);
						if (ret < 0) {
							IMP_LOG_ERR(TAG, "IMP_Encoder_UnRegisterChn(%d) failed\n", 3+chn[i].index);
							return -1;
						}

						ret = IMP_Encoder_DestroyChn(3+chn[i].index);
						if (ret < 0) {
							IMP_LOG_ERR(TAG, "IMP_Encoder_DestroyChn(%d) failed\n", 3+chn[i].index);
							return -1;
						}
					}
				}
			}
		}
		for (i = 0; i < FS_CHN_NUM; i++) {
			if (chn[i].enable) {
				ret = IMP_Encoder_DestroyGroup(chn[i].index);
				if (ret < 0) {
					IMP_LOG_ERR(TAG, "IMP_Encoder_DestroyGroup(%d) failed\n", chn[i].index);
					return -1;
				}
			}
		}
	}

	/* Step.d FrameSource exit */
	{
		for (i = 0; i < FS_CHN_NUM; i++) {
			if (chn[i].enable) {
				ret = IMP_FrameSource_DestroyChn(chn[i].index);
				if (ret < 0) {
					IMP_LOG_ERR(TAG, "IMP_FrameSource_DestroyChn(%d) failed\n", chn[i].index);
					return -1;
				}
			}
		}
	}

	/* Step.e System exit */
	{
		IMP_System_Exit();

		ret = IMP_ISP_DisableSensor();
		if (ret < 0) {
			IMP_LOG_ERR(TAG, "IMP_ISP_DisableSensor failed\n");
			return -1;
		}

		ret = IMP_ISP_DelSensor(&sensor_info);
		if (ret < 0) {
			IMP_LOG_ERR(TAG, "IMP_ISP_DelSensor failed\n");
			return -1;
		}

		ret = IMP_ISP_DisableTuning();
		if (ret < 0) {
			IMP_LOG_ERR(TAG, "IMP_ISP_DisableTuning failed\n");
			return -1;
		}

		ret = IMP_ISP_Close();
		if (ret < 0) {
			IMP_LOG_ERR(TAG, "IMP_ISP_Close failed\n");
			return -1;
		}
	}

	return 0;
}

#ifdef SAVE_STREAM
static int save_stream_test(int fd, IMPEncoderStream *stream)
{
	int i = 0;
	int ret = 0;
	int nr_pack = stream->packCount;

	for (i = 0; i < nr_pack; i++) {
		ret = write(fd, (void *)stream->pack[i].virAddr, stream->pack[i].length);
		if (ret != stream->pack[i].length) {
			close(fd);
			IMP_LOG_ERR(TAG, "stream write failed\n");
			return -1;
		}
	}

	return 0;
}

static int save_stream_by_name_test(char *stream_prefix, int idx, IMPEncoderStream *stream)
{
	int i = 0;
	int ret = 0;
	int stream_fd = -1;
	char stream_path[128] = { 0 };
	int nr_pack = stream->packCount;

	sprintf(stream_path, "%s-%02d.jpg", stream_prefix, idx);

	IMP_LOG_DBG(TAG, "Open Stream file %s ", stream_path);
	stream_fd = open(stream_path, O_RDWR | O_CREAT | O_TRUNC, 0777);
	if (stream_fd < 0) {
		IMP_LOG_ERR(TAG, "open %s failed\n", stream_path);
		return -1;
	}
	IMP_LOG_DBG(TAG, "OK\n");

	for (i = 0; i < nr_pack; i++) {
		ret = write(stream_fd, (void *)stream->pack[i].virAddr, stream->pack[i].length);
		if (ret != stream->pack[i].length) {
			close(stream_fd);
			IMP_LOG_ERR(TAG, "stream write failed\n");
			return -1;
		}
	}

	close(stream_fd);

	return 0;
}
#endif

const IMPEncoderRcMode RcMode[] = { ENC_RC_MODE_FIXQP, ENC_RC_MODE_CBR, ENC_RC_MODE_VBR, ENC_RC_MODE_SMART };
const IMPEncoderFrmRate Fps[] = { {25,1}, {20,1}, {15,1}, {25,2}, {10,1} };
const IMPEncoderGOPSizeCfg GOPSizeCfg[] = { {50}, {40}, {30}, {25}, {20} };
const uint32_t Qp[] = { 40, 25, 35, 15, 30 };
const uint32_t MaxQp[] = { 48, 50, 28, 40, 35 };
const uint32_t MinQp[] = { 15, 35, 12, 25, 20 };
const uint32_t Bitrate[] = { 1024, 512, 4096, 2048, 3072 };

#if 1
#define FRM_BIT_RATE_TIME 2
#define STREAM_TYPE_NUM 3
static int frmrate_sp[STREAM_TYPE_NUM] = { 0 };
static int statime_sp[STREAM_TYPE_NUM] = { 0 };
static int bitrate_sp[STREAM_TYPE_NUM] = { 0 };
#endif

static double cur_fpsnum = 0.0;
static double cur_bitratenum = 0.0;
static void *change_rcparam_chn0_chn1(void *args)
{
	int i = 0, ret = 0;
	char thread_name[32] = { 0 };
	IMPEncoderAttrRcMode pstRcModeCfg;

	int val = (int)args;
	int chnNum = val & 0xffff;

	sprintf(thread_name, "%s-%d", "changerc", chnNum);
	prctl(PR_SET_NAME, (unsigned long)thread_name);

	while (changeRcParam[chnNum] == 1) {
		sem_wait(&sem_countfps);
		cur_fpsnum = Fps[i%5].frmRateNum / Fps[i%5].frmRateDen;
		cur_bitratenum = Bitrate[i%5];
		ret = IMP_Encoder_SetChnFrmRate(chnNum, &Fps[i%5]);
		if (ret < 0) {
			IMP_LOG_ERR(TAG, "IMP_Encoder_SetChnFrmRate(%d) failed\n", chnNum);
			return NULL;
		}

		ret = IMP_Encoder_SetGOPSize(chnNum, &GOPSizeCfg[i%5]);
		if (ret < 0) {
			IMP_LOG_ERR(TAG, "IMP_Encoder_SetGOPSize(%d) failed\n", chnNum);
			return NULL;
		}

		ret = IMP_Encoder_GetChnAttrRcMode(chnNum, &pstRcModeCfg);
		if (ret < 0) {
			IMP_LOG_ERR(TAG, "IMP_Encoder_GetChnAttrRcMode(%d) failed\n", chnNum);
			return NULL;
		}

		pstRcModeCfg.rcMode = RcMode[i%4];

		if (pstRcModeCfg.rcMode == ENC_RC_MODE_FIXQP) {
			pstRcModeCfg.attrH264FixQp.qp = Qp[i%5];
		} else if (pstRcModeCfg.rcMode == ENC_RC_MODE_CBR) {
			pstRcModeCfg.attrH264Cbr.maxQp = MaxQp[i%5];
			pstRcModeCfg.attrH264Cbr.minQp = MinQp[i%5];
			pstRcModeCfg.attrH264Cbr.outBitRate = Bitrate[i%5];
		} else if (pstRcModeCfg.rcMode == ENC_RC_MODE_VBR) {
			pstRcModeCfg.attrH264Vbr.maxQp = MaxQp[i%5];
			pstRcModeCfg.attrH264Vbr.minQp = MinQp[i%5];
			pstRcModeCfg.attrH264Vbr.maxBitRate = Bitrate[i%5];
		} else if (pstRcModeCfg.rcMode == ENC_RC_MODE_SMART) {
			pstRcModeCfg.attrH264Smart.maxQp = MaxQp[i%5];
			pstRcModeCfg.attrH264Smart.minQp = MinQp[i%5];
			pstRcModeCfg.attrH264Smart.maxBitRate = Bitrate[i%5];
		}
		ret = IMP_Encoder_SetChnAttrRcMode(chnNum, &pstRcModeCfg);
		if (ret < 0) {
			IMP_LOG_ERR(TAG, "IMP_Encoder_SetChnAttrRcMode(%d) failed\n", chnNum);
			return NULL;
		}

		sleep(1);

		IMPEncoderFrmRate pstFps;
		ret = IMP_Encoder_GetChnFrmRate(chnNum, &pstFps);
		if (ret < 0) {
			IMP_LOG_ERR(TAG, "IMP_Encoder_GetChnFrmRate(%d) failed\n", chnNum);
			return NULL;
		}

		IMPEncoderGOPSizeCfg pstGOPSizeCfg;
		ret = IMP_Encoder_GetGOPSize(chnNum, &pstGOPSizeCfg);
		if (ret < 0) {
			IMP_LOG_ERR(TAG, "IMP_Encoder_SetGOPSize(%d) failed\n", chnNum);
			return NULL;
		}

		int rc_flag = 0;
		ret = IMP_Encoder_GetChnAttrRcMode(chnNum, &pstRcModeCfg);
		if (ret < 0) {

			IMP_LOG_ERR(TAG, "IMP_Encoder_GetChnAttrRcMode(%d) failed\n", chnNum);
			return NULL;
		}
		if (pstRcModeCfg.rcMode == ENC_RC_MODE_FIXQP) {
			if(pstRcModeCfg.attrH264FixQp.qp == Qp[i%5])
				rc_flag = 1;
		} else if (pstRcModeCfg.rcMode == ENC_RC_MODE_CBR) {
			if (pstRcModeCfg.attrH264Cbr.maxQp == MaxQp[i%5] && pstRcModeCfg.attrH264Cbr.minQp == MinQp[i%5] && pstRcModeCfg.attrH264Cbr.outBitRate == Bitrate[i%5])
				rc_flag = 1;
		} else if (pstRcModeCfg.rcMode == ENC_RC_MODE_VBR) {
			if (pstRcModeCfg.attrH264Vbr.maxQp == MaxQp[i%5] && pstRcModeCfg.attrH264Vbr.minQp == MinQp[i%5] && pstRcModeCfg.attrH264Vbr.maxBitRate == Bitrate[i%5])
				rc_flag = 1;
		} else if (pstRcModeCfg.rcMode == ENC_RC_MODE_SMART) {
			if (pstRcModeCfg.attrH264Smart.maxQp == MaxQp[i%5] && pstRcModeCfg.attrH264Smart.minQp == MinQp[i%5] && pstRcModeCfg.attrH264Smart.maxBitRate == Bitrate[i%5])
				rc_flag = 1;
		}

		if(pstFps.frmRateNum / pstFps.frmRateDen == Fps[i%5].frmRateNum / Fps[i%5].frmRateDen && GOPSizeCfg[i%5].gopsize == pstGOPSizeCfg.gopsize && rc_flag == 1){
			printf("\033[1;31m chn:(%d) change count = %d \033[0m\n", chnNum, i);
		} else {
			int stream_fd = -1;
			stream_fd = open("/tmp/log.txt", O_RDWR | O_CREAT | O_APPEND, 0644);
			if (stream_fd < 0) {
				IMP_LOG_ERR(TAG, "failed open fd!\n");
				return ((void *)-1);
			}
			char error[128];
			sprintf(error, "chn %d_Encoder Change Rc error!!!\n", chnNum);
			ret = write(stream_fd, error, 33);
			if (ret == -1) {
				IMP_LOG_ERR(TAG, "failed write! \n");
				return ((void *)-1);
			}
			close(stream_fd);
#if 1
			printf("\033[1;31m chn:(%d) change error! setFps:curFps=[%d,%d], setGopsize:curGopsize=[%d,%d], rc_flag = %d\033[0m\n",
					chnNum,
					Fps[i%5].frmRateNum / Fps[i%5].frmRateDen, pstFps.frmRateNum / pstFps.frmRateDen,
					GOPSizeCfg[i%5].gopsize, pstGOPSizeCfg.gopsize,
					rc_flag);
#endif
		}
		i++;
	}
	return NULL;

}

static void *get_video_stream_chn0(void *args)
{
	int i = 0;
	int ret = 0;
	char thread_name[32] = { 0 };
#ifdef SAVE_STREAM
	int stream_fd = -1;
	char stream_path[64] = { 0 };
	IMPPayloadType payloadType = (val >> 16) & 0xffff;
#endif
	IMPEncoderStream stream;

	int val = (int)args;
	int chnNum = val & 0xffff;

	sprintf(thread_name, "%s-%d", "getstream", chnNum);
	prctl(PR_SET_NAME, (unsigned long)thread_name);

	ret = IMP_Encoder_StartRecvPic(chnNum);
	if (ret < 0) {
		IMP_LOG_ERR(TAG, "IMP_Encoder_StartRecvPic(%d) failed\n", chnNum);
		goto err_StartRecvPic;
	}

#ifdef SAVE_STREAM
	sprintf(stream_path, "%s/stream-encoder-changeRcParam-%d-%dx%d.%s", STREAM_FILE_PATH_PREFIX, chnNum,
			chn[chnNum].fs_chn_attr.picWidth, chn[chnNum].fs_chn_attr.picHeight,
			(payloadType == PT_H264) ? "h264" : "h265");

	IMP_LOG_DBG(TAG, "Video ChnNum=%d Open Stream file %s ", chnNum, stream_path);
	stream_fd = open(stream_path, O_RDWR | O_CREAT | O_TRUNC, 0777);
	if (stream_fd < 0) {
		IMP_LOG_ERR(TAG, "open %s failed\n", stream_path);
		goto err_open;
	}
	IMP_LOG_DBG(TAG, "OK\n");
#endif

	for (i = 0; i < totalSaveCnt; i++) {
		ret = IMP_Encoder_PollingStream(chnNum, 1000);
		if (ret < 0) {
			IMP_LOG_ERR(TAG, "IMP_Encoder_PollingStream(%d) timeout\n", chnNum);
			continue;
		}

		/* Get H264 or H265 Stream */
		ret = IMP_Encoder_GetStream(chnNum, &stream, 1);
		if (ret < 0) {
			IMP_LOG_ERR(TAG, "IMP_Encoder_GetStream(%d) failed\n", chnNum);
			goto err_GetStream;
		}
		
#ifdef SHOW_FRM_BITRATE       
			int i, len = 0;       
			for (i = 0; i < stream.packCount; i++) {
				len += stream.pack[i].length;  
			}
			bitrate_sp[chnNum] += len;     
			frmrate_sp[chnNum]++; 

			int64_t now = IMP_System_GetTimeStamp() / 1000;
			static int fps_error_cnt = 0;
			if(((int)(now - statime_sp[chnNum]) / 1000) >= FRM_BIT_RATE_TIME){ 
				double fps = (double)frmrate_sp[chnNum] / ((double)(now - statime_sp[chnNum]) / 1000); 
				double kbr = (double)bitrate_sp[chnNum] * 8 / (double)(now - statime_sp[chnNum]);

				int fps_error_flag = 0;
				int kbr_error_flag = 0;

				if(fps < cur_fpsnum * 11 / 10 && fps > cur_fpsnum * 9 / 10) {
					//printf("streamNum[%d]:FPS: %0.2f,Bitrate: %0.2f(kbps)\n", chnNum, fps, kbr);
					printf("fps set success, cur_fpsnum = %f, fps = %f \n", cur_fpsnum, fps);
					fps_error_cnt = 0;
					fps_error_flag = 0;
				} else {
					fps_error_cnt++;
					if(fps_error_cnt > 1) {
						fps_error_flag = 1;
						printf("fps set error , cur_fpsnum = %f, fps = %f\n", cur_fpsnum, fps);
					}
				}

#if 1
				IMPEncoderAttrRcMode pstRcModeCfg;
				ret = IMP_Encoder_GetChnAttrRcMode(chnNum, &pstRcModeCfg);
				if (ret < 0) {
					IMP_LOG_ERR(TAG, "IMP_Encoder_GetChnAttrRcMode(%d) failed\n", chnNum);
					return NULL;
				}
				double max_kbr = 0.0, min_kbr = 0.0;
				if (pstRcModeCfg.rcMode == ENC_RC_MODE_CBR) {
					max_kbr = cur_bitratenum * 11 / 10;
					min_kbr = cur_bitratenum * 9 / 10;
				} else if (pstRcModeCfg.rcMode == ENC_RC_MODE_VBR) {
					max_kbr = cur_bitratenum * 11 / 10;
					min_kbr = 0.0;
				} else if (pstRcModeCfg.rcMode == ENC_RC_MODE_SMART) {
					max_kbr = cur_bitratenum * 11 / 10;
					min_kbr = 0.0;
				}

				if((kbr < max_kbr && kbr > min_kbr) || pstRcModeCfg.rcMode == ENC_RC_MODE_FIXQP) {
					printf("kbr set success, cur_bitratenum = %f, kbr = %f!!! cur rcmode = 0x%x\n", cur_bitratenum, kbr, pstRcModeCfg.rcMode);
					kbr_error_flag = 0;
				} else {
					printf("kbr set error, cur_bitratenum = %f, kbr = %f, min_kbr = %f, max_kbr = %f, cur rcMode = 0x%x\n", cur_bitratenum, kbr, min_kbr, max_kbr, pstRcModeCfg.rcMode);
					kbr_error_flag = 1;
				}

				if (fps_error_flag == 1 || kbr_error_flag == 1){
					int stream_fd = -1; 
					stream_fd = open("/tmp/log.txt", O_RDWR | O_CREAT | O_APPEND, 0644);
					if (stream_fd < 0) {
						IMP_LOG_ERR(TAG, "failed open fd!\n");
						return ((void *)-1);
					}   
					char error[128];
					sprintf(error, "Encoder Moudles Set FPS or Bitrate Error!!!!\n");
					ret = write(stream_fd, error, 45);
					if (ret == -1) {
						IMP_LOG_ERR(TAG, "failed write! \n");
						return ((void *)-1);
					}
					close(stream_fd);
				}
#endif
				frmrate_sp[chnNum] = 0;        
				bitrate_sp[chnNum] = 0;        
				statime_sp[chnNum] = now;
				sem_post(&sem_countfps);
			}
#endif 

#ifdef SAVE_STREAM
		ret = save_stream_test(stream_fd, &stream);
		if (ret < 0) {
			goto err_SaveStream;
		}
#endif

		IMP_Encoder_ReleaseStream(chnNum, &stream);
	}
	getVideo[chnNum+1] = 0;
	getJpeg[chnNum] = 0;
	changeRcParam[chnNum] = 0;
	changeRcParam[chnNum+1] = 0;

#ifdef SAVE_STREAM
	close(stream_fd);
#endif

	ret = IMP_Encoder_StopRecvPic(chnNum);
	if (ret < 0) {
		IMP_LOG_ERR(TAG, "IMP_Encoder_StopRecvPic(%d) failed\n", chnNum);
		goto err_StopRecvPic;
	}

	return NULL;
#ifdef SAVE_STREAM
err_SaveStream:
	IMP_Encoder_ReleaseStream(chnNum, &stream);
#endif
err_StopRecvPic:
err_GetStream:
#ifdef SAVE_STREAM
	close(stream_fd);
err_open:
#endif
	IMP_Encoder_StopRecvPic(chnNum);
err_StartRecvPic:
	return NULL;
}

static void *get_video_stream_chn1(void *args)
{
	int ret = 0;
	char thread_name[32] = { 0 };
#ifdef SAVE_STREAM
	int stream_fd = -1;
	char stream_path[64] = { 0 };
	IMPPayloadType payloadType = (val >> 16) & 0xffff;
#endif
	IMPEncoderStream stream;

	int val = (int)args;
	int chnNum = val & 0xffff;

	sprintf(thread_name, "%s-%d", "getstream", chnNum);
	prctl(PR_SET_NAME, (unsigned long)thread_name);

	ret = IMP_Encoder_StartRecvPic(chnNum);
	if (ret < 0) {
		IMP_LOG_ERR(TAG, "IMP_Encoder_StartRecvPic(%d) failed\n", chnNum);
		goto err_StartRecvPic;
	}

#ifdef SAVE_STREAM
	sprintf(stream_path, "%s/stream-encoder-changeRcParam-%d-%dx%d.%s", STREAM_FILE_PATH_PREFIX, chnNum,
			chn[chnNum].fs_chn_attr.picWidth, chn[chnNum].fs_chn_attr.picHeight,
			(payloadType == PT_H264) ? "h264" : "h265");

	IMP_LOG_DBG(TAG, "Video ChnNum=%d Open Stream file %s ", chnNum, stream_path);
	stream_fd = open(stream_path, O_RDWR | O_CREAT | O_TRUNC, 0777);
	if (stream_fd < 0) {
		IMP_LOG_ERR(TAG, "open %s failed\n", stream_path);
		goto err_open;
	}
	IMP_LOG_DBG(TAG, "OK\n");
#endif

	while (getVideo[chnNum] == 1) {
		ret = IMP_Encoder_PollingStream(chnNum, 1000);
		if (ret < 0) {
			IMP_LOG_ERR(TAG, "IMP_Encoder_PollingStream(%d) timeout\n", chnNum);
			continue;
		}

		/* Get H264 or H265 Stream */
		ret = IMP_Encoder_GetStream(chnNum, &stream, 1);
		if (ret < 0) {
			IMP_LOG_ERR(TAG, "IMP_Encoder_GetStream(%d) failed\n", chnNum);
			goto err_GetStream;
		}

#ifdef SAVE_STREAM
		ret = save_stream_test(stream_fd, &stream);
		if (ret < 0) {
			goto err_SaveStream;
		}
#endif

		IMP_Encoder_ReleaseStream(chnNum, &stream);
	}
#ifdef SAVE_STREAM
	close(stream_fd);
#endif

	ret = IMP_Encoder_StopRecvPic(chnNum);
	if (ret < 0) {
		IMP_LOG_ERR(TAG, "IMP_Encoder_StopRecvPic(%d) failed\n", chnNum);
		goto err_StopRecvPic;
	}

	return NULL;
#ifdef SAVE_STREAM
err_SaveStream:
	IMP_Encoder_ReleaseStream(chnNum, &stream);
#endif
err_StopRecvPic:
err_GetStream:
#ifdef SAVE_STREAM
	close(stream_fd);
err_open:
#endif
	IMP_Encoder_StopRecvPic(chnNum);
err_StartRecvPic:
	return NULL;
}

static void *get_jpeg_stream_chn3(void *args)
{
	srand((unsigned int)time(NULL));
	int i = 0;
	int ret = 0;
	char thread_name[32] = { 0 };
#ifdef SAVE_STREAM
	char stream_path[64] = { 0 };
#endif
	IMPEncoderStream stream;

	int val = (int)args;
	int chnNum = val & 0xffff;

#ifdef SAVE_STREAM
	sprintf(stream_path, "%s/stream-encoder-changeRcParam-%d-%dx%d", STREAM_FILE_PATH_PREFIX, chnNum,
			chn[chnNum-3].fs_chn_attr.picWidth, chn[chnNum-3].fs_chn_attr.picHeight);
#endif

	sprintf(thread_name, "%s-%d", "getstream", chnNum);
	prctl(PR_SET_NAME, (unsigned long)thread_name);

	while (getJpeg[chnNum-3] == 1) {
		ret = IMP_Encoder_StartRecvPic(chnNum);
		if (ret < 0) {
			IMP_LOG_ERR(TAG, "IMP_Encoder_StartRecvPic(%d) failed\n", chnNum);
			goto err_StartRecvPic;
		}

		ret = IMP_Encoder_PollingStream(chnNum, 1000);
		if (ret < 0) {
			IMP_LOG_ERR(TAG, "IMP_Encoder_PollingStream(%d) timeout\n", chnNum);
			continue;
		}

		/* Get JPEG Snap */
		ret = IMP_Encoder_GetStream(chnNum, &stream, 1);
		if (ret < 0) {
			IMP_LOG_ERR(TAG, "IMP_Encoder_GetStream(%d) failed\n", chnNum);
			goto err_GetStream;
		}

#ifdef SAVE_STREAM
		ret = save_stream_by_name_test(stream_path, i, &stream);
		if (ret < 0) {
			IMP_LOG_ERR(TAG, "save_stream_by_name_test failed\n");
			goto err_SaveStream;
		}
#endif

		IMP_Encoder_ReleaseStream(chnNum, &stream);

		ret = IMP_Encoder_StopRecvPic(chnNum);
		if (ret < 0) {
			IMP_LOG_ERR(TAG, "IMP_Encoder_StopRecvPic(%d) failed\n", chnNum);
			goto err_StopRecvPic;
		}
		i++;
		// printf("[%s %d] cnt=%d\n", __func__, __LINE__, i);
		sleep(rand()%10+1);
	}

	return NULL;
#ifdef SAVE_STREAM
err_SaveStream:
	IMP_Encoder_ReleaseStream(chnNum, &stream);
#endif
err_GetStream:
	IMP_Encoder_StopRecvPic(chnNum);
err_StopRecvPic:
err_StartRecvPic:
	return NULL;
}
